package cn.slipi.common.handler;


import cn.slipi.common.enums.ResponseCode;
import cn.slipi.common.response.ErrorResponse;
import cn.slipi.common.response.builder.ResponseBuilder;
import cn.slipi.common.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.*;

@Slf4j
@Component
public class DefaultExceptionHandler implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) {
        if (e instanceof MethodArgumentNotValidException) {
            response.setStatus(ResponseCode.PARAM_IS_INVALID.code());
            ErrorResponse error = ErrorResponse.error(ResponseCode.PARAM_IS_INVALID, e, this.methodArgumentNotValid(e));
            log.error("URL:{},{}！", request.getRequestURI(), ResponseCode.PARAM_IS_INVALID.msg(), e);
            return ResponseBuilder.build(error);
        } else if (e instanceof ConstraintViolationException) {
            response.setStatus(ResponseCode.PARAM_IS_INVALID.code());
            ErrorResponse error = ErrorResponse.error(ResponseCode.PARAM_IS_INVALID, e, this.constraintViolation(e));
            log.error("URL:{},{}！", request.getRequestURI(), ResponseCode.PARAM_IS_INVALID.msg(), e);
            return ResponseBuilder.build(error);
        } else if (e instanceof MissingServletRequestParameterException) {
            response.setStatus(ResponseCode.PARAM_IS_INVALID.code());
            MissingServletRequestParameterException msr = (MissingServletRequestParameterException) e;
            ErrorResponse error = ErrorResponse.error(ResponseCode.PARAM_IS_INVALID, e, String.format("[%s]字段不能为空", msr.getParameterName()));
            log.error("URL:{},{}！", request.getRequestURI(), ResponseCode.PARAM_IS_INVALID.msg(), e);
            return ResponseBuilder.build(error);
        } else if (e instanceof NoHandlerFoundException) {
            response.setStatus(ResponseCode.NOT_FOUND.code());
            log.error("URL:{},{}！", request.getRequestURI(), ResponseCode.NOT_FOUND.msg(), e);
            return ResponseBuilder.build(ErrorResponse.error(ResponseCode.NOT_FOUND, e));
        } else {
            response.setStatus(ResponseCode.SYSTEM_ERROR.code());
            ErrorResponse error = ErrorResponse.error(ResponseCode.SYSTEM_ERROR, e);
            log.error("URL:{},{}！", request.getRequestURI(), ResponseCode.SYSTEM_ERROR.msg(), e);
            return ResponseBuilder.build(error);
        }
    }

    private String methodArgumentNotValid(Exception e) {
        MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
        List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
        List<Map<String, String>> info = new ArrayList<>();
        for (FieldError obj : fieldErrors) {
            Map<String, String> single = new HashMap<>();
            single.put(obj.getField(), obj.getDefaultMessage());
            info.add(single);
        }
        return JsonUtil.object2Json(info);
    }

    private String constraintViolation(Exception e) {
        ConstraintViolationException vex = (ConstraintViolationException) e;
        Set<ConstraintViolation<?>> constraintViolations = vex.getConstraintViolations();
        StringBuffer sbf = new StringBuffer();
        for (ConstraintViolation violation : constraintViolations) {
            String message = violation.getMessage();
            if (StringUtils.isNotBlank(message)) {
                sbf.append(violation.getMessage());
            }
        }
        return sbf.toString();
    }
}
